package track_formats

import (
	"encoding/hex"
	"fmt"
	"log"
	"strconv"
	"strings"
	"time"

	"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
	"github.com/pion/rtp"
	"gogs.smarteye.com/servere/gobvrtp/encoder"
	"gogs.smarteye.com/servere/gobvrtp/track"
)

type AACTrack struct {
	AudioTrack

	ProfileLevelID   int
	Config           mpeg4audio.Config
	SizeLength       int
	IndexLength      int
	IndexDeltaLength int

	encoder *encoder.AACEncoder1
	decoder *encoder.AACDecoder1
}

func NewAACTrack() *AACTrack {
	c := &AACTrack{
		ProfileLevelID:   0,
		Config:           mpeg4audio.Config{},
		SizeLength:       13,
		IndexLength:      3,
		IndexDeltaLength: 3,
	}

	param := c.CtlParma()
	param.SetPayloadType(97)
	param.SetCodec(track.CodecIdAAC)
	param.SetClockRate(48000)
	c.SetChannel(2)
	param.SetOnReadRTPFunc(c.onRTP)

	return c
}

func (c *AACTrack) UnmarshalFormat(ctx *track.FormatContext) error {
	_ = c.AudioTrack.UnmarshalFormat(ctx)

	haveConfig := false

	for key, val := range ctx.GetFMTP().KV {
		switch key {
		case "streamtype":
			if val != "5" { // AudioStream in ISO 14496-1
				return fmt.Errorf("streamtype of AAC must be 5")
			}

		case "mode":
			if strings.ToLower(val) != "aac-hbr" {
				return fmt.Errorf("unsupported AAC mode: %v", val)
			}

		case "profile-level-id":
			if tmp, err := strconv.ParseUint(val, 10, 31); err != nil {
				return fmt.Errorf("invalid profile-level-id: %v", val)
			} else {
				c.ProfileLevelID = int(tmp)
			}

		case "config":
			if enc, err := hex.DecodeString(val); err != nil {
				return fmt.Errorf("invalid AAC config: %v", val)
			} else {
				if err = c.Config.Unmarshal(enc); err != nil {
					return fmt.Errorf("invalid AAC config: %v", val)
				}

				haveConfig = true
			}

		case "sizelength":
			if n, err := strconv.ParseUint(val, 10, 31); err != nil || n > 100 {
				return fmt.Errorf("invalid AAC SizeLength: %v", val)
			} else {
				c.SizeLength = int(n)
			}

		case "indexlength":
			if n, err := strconv.ParseUint(val, 10, 31); err != nil || n > 100 {
				return fmt.Errorf("invalid AAC IndexLength: %v", val)
			} else {
				c.IndexLength = int(n)
			}

		case "indexdeltalength":
			if n, err := strconv.ParseUint(val, 10, 31); err != nil || n > 100 {
				return fmt.Errorf("invalid AAC IndexDeltaLength: %v", val)
			} else {
				c.IndexDeltaLength = int(n)
			}
		}
	}

	if !haveConfig {
		return fmt.Errorf("config is missing")
	}

	if c.SizeLength == 0 {
		return fmt.Errorf("sizelength is missing")
	}

	return nil
}

func (c *AACTrack) SetChannel(channel int) *AACTrack {
	c.CtlParma().SetChannel(channel)
	c.Config.ChannelCount = channel
	return c
}

func (c *AACTrack) SetASC(asc string) *AACTrack {
	enc, err := hex.DecodeString(asc)
	if err != nil {
		log.Println(err)
		return c
	}

	if err = c.Config.Unmarshal(enc); err != nil {
		log.Println(err)
	}

	return c
}

// RTPMap implements Format.
func (c *AACTrack) RTPMap() string {
	sampleRate := c.ClockRate()
	if c.Config.ExtensionSampleRate != 0 {
		sampleRate = c.Config.ExtensionSampleRate
	}

	channelCount := c.Config.ChannelCount
	if c.Config.ExtensionType == mpeg4audio.ObjectTypePS {
		channelCount = 2
	}

	return fmt.Sprintf("%v/%v/%v", c.Codec(), sampleRate, channelCount)
}

// FMTP implements Format.
func (c *AACTrack) FMTP() map[string]string {
	f := c

	profileLevelID := f.ProfileLevelID
	if profileLevelID == 0 { // support legacy definition which didn't include profile-level-id
		profileLevelID = 1
	}

	fmtp := map[string]string{
		"streamtype":       "5",
		"mode":             "AAC-hbr",
		"profile-level-id": strconv.FormatInt(int64(profileLevelID), 10),
	}

	if f.SizeLength > 0 {
		fmtp["sizelength"] = strconv.FormatInt(int64(f.SizeLength), 10)
	}

	if f.IndexLength > 0 {
		fmtp["indexlength"] = strconv.FormatInt(int64(f.IndexLength), 10)
	}

	if f.IndexDeltaLength > 0 {
		fmtp["indexdeltalength"] = strconv.FormatInt(int64(f.IndexDeltaLength), 10)
	}

	if enc, err := f.Config.Marshal(); err == nil {
		fmtp["config"] = hex.EncodeToString(enc)
	}

	return fmtp
}

func (c *AACTrack) WritePacket(t time.Time, data []byte) error {
	param := c.CtlParma()

	if c.encoder == nil {
		// 初始化编码器
		e := &encoder.AACEncoder1{
			SampleRate:            c.ClockRate(),
			PayloadType:           c.PayloadType(),
			SSRC:                  ToPtr(c.SSRC()),
			InitialSequenceNumber: ToPtr(param.InitialSequenceNumber),
			InitialTimestamp:      nil,
			PayloadMaxSize:        param.PayloadMaxSize,
		}

		_ = e.Init()
		c.encoder = e
	}

	packets, err := c.encoder.Encode([][]byte{data}, 0)
	if err != nil {
		return err
	}

	timestamp, _ := c.CtlParma().RtpTimeEncode(t)

	for _, packet := range packets {
		packet.Timestamp = timestamp
		if err = c.WriteRTP(packet); err != nil {
			return err
		}
	}

	return nil
}

func (c *AACTrack) onRTP(_ track.Track, pkt *rtp.Packet) {
	packets, _ := c.Detector(pkt)

	if c.decoder == nil {
		// 初始化解码器
		decoder := &encoder.AACDecoder1{
			SampleRate:       c.ClockRate(),
			SizeLength:       c.SizeLength,
			IndexLength:      c.IndexLength,
			IndexDeltaLength: c.IndexDeltaLength,
		}
		_ = decoder.Init()
		c.decoder = decoder
	}

	for _, packet := range packets {
		if data, err := c.decoder.Decode(packet); err == nil {
			for _, nalu := range data {
				c.onFrame(packet, nalu)
			}
		} else {
			log.Println(err)
		}
	}
}

func init() {
	RegisterTrack(track.CodecIdAAC, func() track.Track {
		return NewAACTrack()
	})
}
